home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-06-30 | 20.2 KB | 779 lines |
- /*
- * @(#)DefaultCaret.java 1.52 98/04/09
- *
- * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
- *
- * This software is the confidential and proprietary information of Sun
- * Microsystems, Inc. ("Confidential Information"). You shall not
- * disclose such Confidential Information and shall use it only in
- * accordance with the terms of the license agreement you entered into
- * with Sun.
- *
- * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
- * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
- * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
- * THIS SOFTWARE OR ITS DERIVATIVES.
- *
- */
- package com.sun.java.swing.text;
-
- import java.awt.*;
- import java.awt.event.*;
- import java.beans.*;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.io.*;
- import com.sun.java.swing.*;
- import com.sun.java.swing.event.*;
- import com.sun.java.swing.plaf.*;
-
- /**
- * An implementation of Caret for a view that maps over
- * the entire portion of the model represented (i.e. there are no
- * holes in the area represented) and renders the insert position
- * as a vertical line.
- *
- * The foreground color of the component is the color of the caret
- * and the background color of the component is the color of the
- * selections made by moving the caret. The Highlighter implementation
- * of the associated UI is used to actually render the selection.
- * <p>
- * Warning: serialized objects of this class will not be compatible with
- * future swing releases. The current serialization support is appropriate
- * for short term storage or RMI between Swing1.0 applications. It will
- * not be possible to load serialized Swing1.0 objects with future releases
- * of Swing. The JDK1.2 release of Swing will be the compatibility
- * baseline for the serialized form of Swing objects.
- *
- * @author Timothy Prinzing
- * @version 1.52 04/09/98
- * @see Caret
- */
- public class DefaultCaret implements Caret, Serializable, FocusListener, MouseListener, MouseMotionListener {
-
- /**
- * Constructs a default caret.
- */
- public DefaultCaret() {
- }
-
- /**
- * Gets the editor component that this caret is for.
- *
- * @return the component
- */
- protected final JTextComponent getComponent() {
- return component;
- }
-
- /**
- * Damages the area surrounding the caret to cause
- * it to be repainted. If paint() is reimplemented,
- * this method should also be reimplemented.
- *
- * @param r the current location of the caret
- * @see #paint
- */
- protected void damage(Rectangle r) {
- if (r != null) {
- component.repaint(r.x - 1, r.y, 3, r.height);
- }
- }
-
- /**
- * Scrolls the associated view (if necessary) to make
- * the caret visible. Since how this should be done
- * is somewhat of a policy, this method can be
- * reimplemented to change the behavior. By default
- * the scrollRectToVisible method is called on the
- * associated component.
- *
- * @param nloc the new position to scroll to
- */
- protected void adjustVisibility(Rectangle nloc) {
- SwingUtilities.invokeLater(new SafeScroller(nloc));
- }
-
- /**
- * Gets the painter for the Highlighter.
- *
- * @return the painter
- */
- protected Highlighter.HighlightPainter getSelectionPainter() {
- Highlighter.HighlightPainter p = new DefaultHighlighter.DefaultHighlightPainter(
- component.getSelectionColor());
- return p;
- }
-
- /**
- * Tries to set the position of the caret from
- * the coordinates of a mouse event, using viewToModel().
- *
- * @param e the mouse event
- */
- protected void positionCaret(MouseEvent e) {
- Point pt = new Point(e.getX(), e.getY());
- int pos = component.viewToModel(pt);
- if (pos >= 0) {
- setDot(pos);
-
- // clear the prefferred caret position
- // see: JCaret's UpAction/DownAction
- setMagicCaretPosition(null);
- }
- }
-
- /**
- * Tries to move the position of the caret from
- * the coordinates of a mouse event, using viewToModel().
- * This will cause a selection if the dot and mark
- * are different.
- *
- * @param e the mouse event
- */
- protected void moveCaret(MouseEvent e) {
- Point pt = new Point(e.getX(), e.getY());
- int pos = component.viewToModel(pt);
- if (pos >= 0) {
- moveDot(pos);
- }
- }
-
- // --- FocusListener methods --------------------------
-
- /**
- * Called when the component containing the caret gains
- * focus. This is implemented to set the caret to visible
- * if the component is editable, and sets the selection
- * to visible.
- *
- * @param e the focus event
- * @see FocusListener#focusGained
- */
- public void focusGained(FocusEvent e) {
- if (component.isEditable()) {
- setVisible(true);
- }
- //setSelectionVisible(true);
- }
-
- /**
- * Called when the component containing the caret loses
- * focus. This is implemented to set the caret to visibility
- * to false, and to set the selection visibility to false.
- *
- * @param e the focus event
- * @see FocusListener#focusLost
- */
- public void focusLost(FocusEvent e) {
- setVisible(false);
- //setSelectionVisible(false);
- }
-
- // --- MouseListener methods -----------------------------------
-
- /**
- * Called when the mouse is clicked. A double click selects a word,
- * and a triple click the current line.
- *
- * @param e the mouse event
- * @see MouseListener#mouseClicked
- */
- public void mouseClicked(MouseEvent e) {
- if(e.getClickCount() == 2) {
- Action a = new DefaultEditorKit.SelectWordAction();
- a.actionPerformed(null);
- } else if(e.getClickCount() == 3) {
- Action a = new DefaultEditorKit.SelectLineAction();
- a.actionPerformed(null);
- }
- }
-
- /**
- * Requests focus on the associated
- * text component, and tries to set the cursor position.
- *
- * @param e the mouse event
- * @see MouseListener#mousePressed
- */
- public void mousePressed(MouseEvent e) {
- positionCaret(e);
- if (component.isEnabled()) {
- component.requestFocus();
- }
- }
-
- /**
- * Called when the mouse is released.
- *
- * @param e the mouse event
- * @see MouseListener#mouseReleased
- */
- public void mouseReleased(MouseEvent e) {
- }
-
- /**
- * Called when the mouse enters a region.
- *
- * @param e the mouse event
- * @see MouseListener#mouseEntered
- */
- public void mouseEntered(MouseEvent e) {
- }
-
- /**
- * Called when the mouse exits a region.
- *
- * @param e the mouse event
- * @see MouseListener#mouseExited
- */
- public void mouseExited(MouseEvent e) {
- }
-
- // --- MouseMotionListener methods -------------------------
-
- /**
- * Moves the caret position
- * according to the mouse pointer's current
- * location. This effectively extends the
- * selection.
- *
- * @param e the mouse event
- * @see MouseMotionListener#mouseDragged
- */
- public void mouseDragged(MouseEvent e) {
- moveCaret(e);
- }
-
- /**
- * Called when the mouse is moved.
- *
- * @param e the mouse event
- * @see MouseMotionListener#mouseMoved
- */
- public void mouseMoved(MouseEvent e) {
- }
-
- // ---- Caret methods ---------------------------------
-
- /**
- * Renders the caret as a vertical line. If this is reimplemented
- * the damage method should also be reimplemented as it assumes the
- * shape of the caret is a vertical line. Sets the caret color to
- * the value returned by getCaretColor().
- *
- * @param g the graphics context
- * @see #damage
- */
- public void paint(Graphics g) {
- if(isVisible()) {
- try {
- TextUI mapper = component.getUI();
- Rectangle r = mapper.modelToView(dot);
- g.setColor(component.getCaretColor());
- g.drawLine(r.x, r.y, r.x, r.y + r.height - 1);
- } catch (BadLocationException e) {
- // can't render I guess
- //System.err.println("Can't render cursor");
- }
- }
- }
-
- /**
- * Called when the UI is being installed into the
- * interface of a JTextComponent. This can be used
- * to gain access to the model that is being navigated
- * by the implementation of this interface. Sets the dot
- * and mark to 0, and establishes document, property change,
- * focus, mouse, and mouse motion listeners.
- *
- * @param c the component
- * @see Caret#install
- */
- public void install(JTextComponent c) {
- component = c;
- Document doc = c.getDocument();
- dot = mark = 0;
- if (doc != null) {
- doc.addDocumentListener(updateHandler);
- }
- c.addPropertyChangeListener(updateHandler);
- c.addFocusListener(this);
- c.addMouseListener(this);
- c.addMouseMotionListener(this);
- }
-
- /**
- * Called when the UI is being removed from the
- * interface of a JTextComponent. This is used to
- * unregister any listeners that were attached.
- *
- * @param c the component
- * @see Caret#deinstall
- */
- public void deinstall(JTextComponent c) {
- c.removeMouseListener(this);
- c.removeMouseMotionListener(this);
- c.removeFocusListener(this);
- c.removePropertyChangeListener(updateHandler);
- Document doc = c.getDocument();
- if (doc != null) {
- doc.removeDocumentListener(updateHandler);
- }
- component = null;
- if (flasher != null) {
- flasher.stop();
- }
- }
-
- /**
- * Adds a listener to track whenever the caret position has
- * been changed.
- *
- * @param l the listener
- * @see Caret#addChangeListener
- */
- public void addChangeListener(ChangeListener l) {
- listenerList.add(ChangeListener.class, l);
- }
-
- /**
- * Removes a listener that was tracking caret position changes.
- *
- * @param l the listener
- * @see Caret#removeChangeListener
- */
- public void removeChangeListener(ChangeListener l) {
- listenerList.remove(ChangeListener.class, l);
- }
-
- /**
- * Notifies all listeners that have registered interest for
- * notification on this event type. The event instance
- * is lazily created using the parameters passed into
- * the fire method. The listener list is processed last to first.
- *
- * @see EventListenerList
- */
- protected void fireStateChanged() {
- // Guaranteed to return a non-null array
- Object[] listeners = listenerList.getListenerList();
- // Process the listeners last to first, notifying
- // those that are interested in this event
- for (int i = listeners.length-2; i>=0; i-=2) {
- if (listeners[i]==ChangeListener.class) {
- // Lazily create the event:
- if (changeEvent == null)
- changeEvent = new ChangeEvent(this);
- ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
- }
- }
- }
-
- /**
- * Changes the selection visibility.
- *
- * @param vis the new visibility
- */
- public void setSelectionVisible(boolean vis) {
- if (vis) {
- // show
- if ((selectionTag == null) && (dot != mark)) {
- Highlighter h = component.getHighlighter();
- int p0 = Math.min(dot, mark);
- int p1 = Math.max(dot, mark);
- Highlighter.HighlightPainter p = getSelectionPainter();
- try {
- selectionTag = h.addHighlight(p0, p1, p);
- } catch (BadLocationException bl) {
- selectionTag = null;
- }
- }
- } else {
- // hide
- if (selectionTag != null) {
- Highlighter h = component.getHighlighter();
- h.removeHighlight(selectionTag);
- selectionTag = null;
- }
- }
- }
-
- /**
- * Checks whether the current selection is visible.
- *
- * @return true if the selection is visible
- */
- public boolean isSelectionVisible() {
- return (selectionTag != null);
- }
-
- /**
- * Determines if the caret is currently visible.
- *
- * @return true if visible else false
- * @see Caret#isVisible
- */
- public boolean isVisible() {
- return visible;
- }
-
- /**
- * Sets the caret visibility, and repaints the caret.
- *
- * @param e the visibility specifier
- * @see Caret#setVisible
- */
- public void setVisible(boolean e) {
- TextUI mapper = component.getUI();
- Document doc = component.getDocument();
- if ((visible != e) && (doc != null) && (mapper != null)) {
- // repaint the caret
- try {
- Rectangle loc = mapper.modelToView(dot);
- damage(loc);
- } catch (BadLocationException badloc) {
- // hmm... not legally positioned
- }
- }
- visible = e;
-
- if (flasher != null) {
- if (visible) {
- flasher.start();
- } else {
- flasher.stop();
- }
- }
- }
-
- /**
- * Sets the caret blink rate.
- *
- * @param rate the rate in milliseconds, 0 to stop blinking
- * @see Caret#setBlinkRate
- */
- public void setBlinkRate(int rate) {
- if (rate != 0) {
- if (flasher == null) {
- flasher = new Timer(rate, updateHandler);
- }
- flasher.setDelay(rate);
- } else {
- if (flasher != null) {
- flasher.stop();
- flasher.removeActionListener(updateHandler);
- flasher = null;
- }
- }
- }
-
- /**
- * Gets the caret blink rate.
- *
- * @returns the delay in milliseconds. If this is
- * zero the caret will not blink.
- * @see Caret#getBlinkRate
- */
- public int getBlinkRate() {
- return (flasher == null) ? 0 : flasher.getDelay();
- }
-
- /**
- * Fetches the current position of the caret.
- *
- * @return the position >= 0
- * @see Caret#getDot
- */
- public int getDot() {
- return dot;
- }
-
- /**
- * Fetches the current position of the mark. If there is a selection,
- * the dot and mark will not be the same.
- *
- * @return the position >= 0
- * @see Caret#getMark
- */
- public int getMark() {
- return mark;
- }
-
- /**
- * Sets the caret position and mark to some position. This
- * implicitly sets the selection range to zero.
- *
- * @param dot the position >= 0
- * @see Caret#setDot
- */
- public void setDot(int dot) {
- // move dot, if it changed
- Document doc = component.getDocument();
- if (doc != null) {
- dot = Math.min(dot, doc.getLength());
- }
- dot = Math.max(dot, 0);
- mark = dot;
- if (this.dot != dot || selectionTag != null) {
- changeCaretPosition(dot);
- }
- if (selectionTag != null) {
- Highlighter h = component.getHighlighter();
- h.removeHighlight(selectionTag);
- selectionTag = null;
- }
- }
-
- /**
- * Moves the caret position to some other position.
- *
- * @param dot the position >= 0
- * @see Caret#moveDot
- */
- public void moveDot(int dot) {
- if (dot != this.dot) {
- changeCaretPosition(dot);
- Highlighter h = component.getHighlighter();
- int p0 = Math.min(dot, mark);
- int p1 = Math.max(dot, mark);
- try {
- if (selectionTag != null) {
- h.changeHighlight(selectionTag, p0, p1);
- } else {
- Highlighter.HighlightPainter p = getSelectionPainter();
- selectionTag = h.addHighlight(p0, p1, p);
- }
- } catch (BadLocationException e) {
- throw new StateInvariantError("Bad caret position");
- }
- }
- }
-
- // ---- local methods --------------------------------------------
-
- /**
- * Sets the caret position (dot) to a new location. This
- * causes the old and new location to be repainted. It
- * also makes sure that the caret is within the visible
- * region of the view, if the view is scrollable.
- */
- void changeCaretPosition(int dot) {
- TextUI mapper = component.getUI();
- Document doc = component.getDocument();
-
- if ((mapper != null) && (doc != null)) {
- // repaint the old position
- Rectangle oldLoc;
- try {
- oldLoc = mapper.modelToView(this.dot);
- damage(oldLoc);
- } catch (BadLocationException e) {
- oldLoc = null;
- }
-
- // set the new value of dot
- this.dot = dot;
-
- // determine the new location and scroll if
- // not visible.
- Rectangle newLoc;
- try {
- newLoc = mapper.modelToView(this.dot);
- } catch (BadLocationException e) {
- newLoc = null;
- }
- if (newLoc != null) {
- adjustVisibility(newLoc);
- }
-
- // repaint the new position
- damage(newLoc);
-
- // notify listeners that the caret moved
- fireStateChanged();
- }
- }
-
- /**
- * Saves the current caret position. This is used when
- * caret up/down actions occur, moving between lines
- * that have uneven end positions.
- *
- * @param p the position
- * @see #getMagicCaretPosition
- * @see UpAction
- * @see DownAction
- */
- public void setMagicCaretPosition(Point p) {
- magicCaretPosition = p;
- }
-
- /**
- * Gets the saved caret position.
- *
- * @return the position
- * see #setMagicCaretPosition
- */
- public Point getMagicCaretPosition() {
- return magicCaretPosition;
- }
-
- // --- serialization ---------------------------------------------
-
- private void readObject(ObjectInputStream s)
- throws ClassNotFoundException, IOException
- {
- s.defaultReadObject();
- updateHandler = new UpdateHandler();
- }
-
- // ---- member variables ------------------------------------------
-
- /**
- * The event listener list.
- */
- protected EventListenerList listenerList = new EventListenerList();
-
- /**
- * The change event for the model.
- * Only one ChangeEvent is needed per model instance since the
- * event's only (read-only) state is the source property. The source
- * of events generated here is always "this".
- */
- protected ChangeEvent changeEvent = null;
-
- // package-private to avoid inner classes private member
- // access bug
- JTextComponent component;
- boolean visible;
- int dot;
- int mark;
- Object selectionTag;
- Timer flasher;
- Point magicCaretPosition;
- transient UpdateHandler updateHandler = new UpdateHandler();
-
- class SafeScroller implements Runnable {
-
- SafeScroller(Rectangle r) {
- this.r = r;
- }
-
- public void run() {
- component.scrollRectToVisible(r);
- }
-
- Rectangle r;
- }
-
- class UpdateHandler implements PropertyChangeListener, DocumentListener, ActionListener {
-
- // --- ActionListener methods ----------------------------------
-
- /**
- * Invoked when the blink timer fires.
- *
- * @param e the action event
- */
- public void actionPerformed(ActionEvent e) {
- visible = !visible;
- Rectangle loc;
- try {
- TextUI mapper = component.getUI();
- loc = mapper.modelToView(dot);
- damage(loc);
- } catch (BadLocationException bl) {
- // hmm....
- }
- }
-
- // --- DocumentListener methods --------------------------------
-
- /**
- * Updates the dot and mark if they were changed by
- * the insertion.
- *
- * @param e the document event
- * @see DocumentListener#insertUpdate
- */
- public void insertUpdate(DocumentEvent e) {
- int adjust = 0;
- int offset = e.getOffset();
- int length = e.getLength();
- if (dot >= offset) {
- adjust = length;
- }
- if (mark >= offset) {
- mark += length;
- }
-
- if (adjust != 0) {
- changeCaretPosition(dot + adjust);
- }
- }
-
- /**
- * Updates the dot and mark if they were changed
- * by the removal.
- *
- * @param e the document event
- * @see DocumentListener#removeUpdate
- */
- public void removeUpdate(DocumentEvent e) {
- int adjust = 0;
- int offs0 = e.getOffset();
- int offs1 = offs0 + e.getLength();
- if (dot >= offs1) {
- adjust = offs1 - offs0;
- } else if (dot >= offs0) {
- adjust = dot - offs0;
- }
- if (mark >= offs1) {
- mark -= offs1 - offs0;
- } else if (mark >= offs0) {
- mark = offs0;
- }
-
- if (mark == (dot - adjust)) {
- setDot(dot - adjust);
- } else {
- changeCaretPosition(dot - adjust);
- }
- }
-
- /**
- * Gives notification that an attribute or set of attributes changed.
- *
- * @param e the document event
- * @see DocumentListener#changedUpdate
- */
- public void changedUpdate(DocumentEvent e) {
- }
-
- // --- PropertyChangeListener methods -----------------------
-
- /**
- * This method gets called when a bound property is changed.
- * We are looking for document changes on the editor.
- */
- public void propertyChange(PropertyChangeEvent evt) {
- Object oldValue = evt.getOldValue();
- Object newValue = evt.getNewValue();
- if ((oldValue instanceof Document) || (newValue instanceof Document)) {
- setDot(0);
- if (oldValue != null) {
- ((Document)oldValue).removeDocumentListener(this);
- }
- if (newValue != null) {
- ((Document)newValue).addDocumentListener(this);
- }
- }
- }
-
- }
- }
-
-